﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        Triangle3D t;

        Matrix4x4 scale;            //缩放矩阵
        Matrix4x4 rotateY;          //Y轴旋转矩阵
        Matrix4x4 rotateX;          //X轴旋转矩阵
        Matrix4x4 rotateZ;          //Z轴旋转矩阵
        Matrix4x4 view;             //摄像机矩阵（平移矩阵）
        Matrix4x4 projection;       //投影矩阵
        Matrix4x4 translate;        //平移矩阵

        public Form1()
        {
            InitializeComponent();

            //初始化缩放矩阵
            scale = new Matrix4x4();
            scale[1, 1] = 100;
            scale[2, 2] = 100;
            scale[3, 3] = 100;
            scale[4, 4] = 1;

            //初始化旋转矩阵，也就是平移矩阵，平移模型的点，所以只有z轴发生变化。
            rotateY = new Matrix4x4();  //y轴旋转
            rotateX = new Matrix4x4();  //x轴旋转
            rotateZ = new Matrix4x4();  //z轴旋转

            //初始化视口矩阵，这里采用平移模型点的方法，模拟架设相机的效果。
            //只是z轴向前平移了150个单位，摄像机矩阵，没有定义摄像机的位置，而只是将模型的各个点向前移动了150单位，用此矩阵相互即可。
            view = new Matrix4x4();
            view[1, 1] = 1;
            view[2, 2] = 1;
            view[3, 3] = 1;
            view[4, 4] = 1;
            view[4, 3] = 150;  //z轴发生平移，此时摄像机的还在原点，模型的每个点，被平移到远离摄像机150的距离。

            //初始化透视矩阵，这个为什么设置成这个矩阵的样子呢？
            //我知道了，这个就是透视矩阵，当(x,y,z,1)乘以这个矩阵之后，就得到了点(x,y,z,z/d)。
            //透视矩阵被定义为：
            /*
            | 1,0,0,0  |
            | 0,1,0,0  |
            | 0,0,1,1/d|
            | 0,0,0,0  |
             这里的d为摄像机(也就是人眼)距离近平面(也就是投影面)的距离。
             当点(x,y,z,1)乘以这个矩阵之后得到(x,y,z,z/d)，然后将其中的每个分量除以第四个维度的分量得到，(xd/z,yd/z,d,1)
             这个你可以看到，就得到了点(x,y,z)的点，通过相似三角形映射到投影面之后的点的坐标了(xd/z,yd/z,d,1)，近似比为d/z。
             所以我们引入了一个透视除法的概念，通过点乘以透视矩阵，然后再除以第四维分量，得到的是，点映射到近平面的点的坐标，此点
             还没有被规范化的cvv空间内。
            */

            projection = new Matrix4x4();
            projection[1, 1] = 1;
            projection[2, 2] = 1;
            projection[3, 3] = 1;
            projection[3, 4] = 1.0 / 150; 


            //平移矩阵
            translate = new Matrix4x4();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //三个点的坐标，这个坐标很小，但是下面使用scale矩阵进行缩放，所以你可以看到较大的三角形
            Vector4 a = new Vector4(0, -0.5, 0, 1); //这里我们的z轴坐标都是0
            Vector4 b = new Vector4(0.5, 0.5, 0, 1);
            Vector4 c = new Vector4(-0.5, 0.5, 0 ,1);  
            t = new Triangle3D(a, b, c);
            t.Transform(scale);
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            t.Draw(e.Graphics);
        }

        int a = 0;
        float b = 0;
        private void timer1_Tick(object sender, EventArgs e)
        {
            a += 2; //这个会溢出，要限定在一定范围。
            b += 0.2f;
            double angle = a / 360.0 * Math.PI; //变成弧度

            //================Y轴初始化
            rotateY[1, 1] = Math.Cos(angle);
            rotateY[1, 3] = Math.Sin(angle);
            rotateY[2, 2] = 1;
            rotateY[3, 1] = -Math.Sin(angle);
            rotateY[3, 3] = Math.Cos(angle);
            rotateY[4, 4] = 1;

            //================X轴初始化
            rotateX[2, 2] = Math.Cos(angle);
            rotateX[2, 3] = Math.Sin(angle);
            rotateX[3, 2] = -Math.Sin(angle);
            rotateX[3, 3] = Math.Cos(angle);
            rotateX[1, 1] = 1;
            rotateX[4, 4] = 1;

            //================Z轴初始化
            rotateZ[1, 1] = Math.Cos(angle);
            rotateZ[1, 2] = Math.Sin(angle);
            rotateZ[2, 1] = -Math.Sin(angle);
            rotateZ[2, 2] = Math.Cos(angle);
            rotateZ[3, 3] = 1;
            rotateZ[4, 4] = 1;


            translate[1, 1] = 1;
            translate[2, 2] = 1;
            translate[3, 3] = 1;
            translate[4, 4] = 1;
            translate[4, 1] = b;     //只进行x轴进行平移

            Matrix4x4 mall = rotateY; //三个方向的旋转
            Matrix4x4 m = scale.Mul(mall);        //z到这一步是可以旋转了

            //每个timer1_Tick执行一次光照计算，然后在Form1_Paint执行画三角形的时候，会使用到点积值*颜色进行绘制
            t.CalculateLighting(m, new Vector4(-1, 1, -1, 0));      //求得世界的法向量

            m = m.Mul(view);                      //执行到这一步没有什么特别的效果，因为只是z轴的移动
            m = m.Mul(projection);                //执行到这一步才能看到3D的效果，因为做了透视变换，有近大远小的效果了
            //m = m.Mul(translate);                 //这里可以进行平移了
            t.Transform(m);
            this.Invalidate();
        }

        private void trackBar1_Scroll(object sender, EventArgs e)
        {
            view[4, 3] = (sender as TrackBar).Value;
        }
    }
}
